package com.android.volley.toolbox;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.conn.ConnectTimeoutException;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.SystemClock;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkError;
import com.android.volley.NetworkResponse;
import com.android.volley.NoConnectionError;
import com.android.volley.Request;
import com.android.volley.ServerError;
import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;

/**
 * @description 图标请求类( 处理网络图片(不适合大图片)、apk图标、app图标、sd上的文件)
 * @author <a href="mailto:zhongyang.zhao@downjoy.com">Barry.Zhao</a>
 * @time 2014-6-7 下午2:38:34
 * @version 6.9
 */
public class BasicImageNetwork extends BasicNetwork {

	private DiskImageBaseCache mCache;

	public BasicImageNetwork(HttpStack httpStack, ByteArrayPool pool, DiskImageBaseCache cache) {
		super(httpStack, pool);
		this.mCache = cache;
	}

	public BasicImageNetwork(HttpStack httpStack, DiskImageBaseCache cache) {
		super(httpStack);
		this.mCache = cache;
	}

	@Override
	public NetworkResponse performRequest(Request<?> request) throws VolleyError {
		String url = request.getUrl();
		Uri uri = Uri.parse(url);
		String scheme = uri.getScheme();
		// 网络图片
		if ("http".equals(scheme) || "https".equals(scheme)) {
			return requestImage(request);
		} else {
			Map<String, String> headers = new HashMap<String, String>();
			headers.put("Cache-Control", "max-age=" + 365 * 24 * 60 * 60);
			String cacheKey = request.getCacheKey();
			File imageFile = mCache.getImageFileForKey(cacheKey);
			File file = new File(uri.getPath());
			copyFile(file, imageFile);
			byte[] data = null;
			try {
				data = imageFile.exists() ? imageFile.getAbsolutePath().getBytes("UTF-8") : new byte[0];
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
				data = new byte[0];
			}
			return new NetworkResponse(200, data, headers, false);
		}
	}

	private void copyFile(File srcFile, File dstFile) {
		if (srcFile.exists()) {
			InputStream in = null;
			FileOutputStream out = null;
			try {
				in = new FileInputStream(srcFile); // 读入原文件
				if (dstFile.exists()) {
					dstFile.delete();
				}
				dstFile.createNewFile();
				out = new FileOutputStream(dstFile);
				byte[] buffer = new byte[1024 * 4];
				int count;
				while ((count = in.read(buffer)) != -1) {
					out.write(buffer, 0, count);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (out != null) {
						out.flush();
						out.close();
						out = null;
					}
					if (in != null) {
						in.close();
						in = null;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

	private NetworkResponse requestImage(Request<?> request) throws VolleyError {
		long requestStart = SystemClock.elapsedRealtime();
		while (true) {
			HttpResponse httpResponse = null;
			byte[] responseContents = null;
			Map<String, String> responseHeaders = new HashMap<String, String>();
			try {
				// Gather headers.
				Map<String, String> headers = new HashMap<String, String>();
				addCacheHeaders(headers, request.getCacheEntry());
				httpResponse = mHttpStack.performRequest(request, headers);
				StatusLine statusLine = httpResponse.getStatusLine();
				int statusCode = statusLine.getStatusCode();

				responseHeaders = convertHeaders(httpResponse.getAllHeaders());
				// Handle cache validation.
				if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
					return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, request.getCacheEntry() == null ? null : request.getCacheEntry().data,
							responseHeaders, true);
				}
				// Some responses such as 204s do not have content. We must
				// check.
				if (httpResponse.getEntity() != null) {
					responseContents = entityToStorage(request, httpResponse.getEntity());
				} else {
					// Add 0 byte response as a way of honestly representing a
					// no-content request.
					responseContents = new byte[0];
				}

				// if the request is slow, log it.
				long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
				logSlowRequests(requestLifetime, request, responseContents, statusLine);

				if (statusCode < 200 || statusCode > 299) {
					throw new IOException();
				}
				return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
			} catch (SocketTimeoutException e) {
				attemptRetryOnException("socket", request, new TimeoutError());
			} catch (ConnectTimeoutException e) {
				attemptRetryOnException("connection", request, new TimeoutError());
			} catch (MalformedURLException e) {
				throw new RuntimeException("Bad URL " + request.getUrl(), e);
			} catch (IOException e) {
				int statusCode = 0;
				NetworkResponse networkResponse = null;
				if (httpResponse != null) {
					statusCode = httpResponse.getStatusLine().getStatusCode();
				} else {
					throw new NoConnectionError(e);
				}
				VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
				if (responseContents != null) {
					networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false);
					if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) {
						attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
					} else {
						// TODO: Only throw ServerError for 5xx status codes.
						throw new ServerError(networkResponse);
					}
				} else {
					throw new NetworkError(networkResponse);
				}
			}
		}
	}

	/** Reads the contents of HttpEntity into a byte[]. */
	private byte[] entityToStorage(Request<?> request, HttpEntity entity) throws IOException, ServerError {
		String cacheKey = request.getCacheKey();
		File destFile = mCache.getImageFileForKey(cacheKey);
		long contentLength = entity.getContentLength();
		long fileLength = destFile.exists() ? destFile.length() : 0;
		// if has the same length , return the exist file
		if (contentLength > 0 && contentLength == fileLength) {
			return destFile.getAbsolutePath().getBytes();
		}
		// delete exist file
		if (destFile.exists()) {
			mCache.remove(cacheKey);
		}
		FileOutputStream out = null;
		try {
			InputStream in = entity.getContent();
			if (in == null) {
				throw new ServerError();
			}
			File parent = destFile.getParentFile();
			if (!parent.exists()) {
				parent.mkdirs();
			}
			destFile.createNewFile();
			out = new FileOutputStream(destFile);
			byte[] buffer = new byte[1024 * 4];
			int count;
			while ((count = in.read(buffer)) != -1) {
				out.write(buffer, 0, count);
			}
			return destFile.getAbsolutePath().getBytes();
		} catch (Exception e) {
			e.printStackTrace();
			destFile.delete();
			return new byte[0];
		} finally {
			if (out != null) {
				out.flush();
				out.close();
				out = null;
			}
			try {
				// Close the InputStream and release the resources by
				// "consuming the content".
				entity.consumeContent();
			} catch (IOException e) {
				// This can happen if there was an exception above that left the
				// entity in
				// an invalid state.
				VolleyLog.v("Error occured when calling consumingContent");
			}

		}
	}

	private boolean saveBitmapToStorage(File destFile, Bitmap bitmap) {
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(destFile);
			return bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
		} catch (Exception e) {
			e.printStackTrace();
			destFile.delete();
			return false;
		} finally {
			try {
				out.flush();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			out = null;
		}

	}
}
